// -*-c#-*-
// License: New BSD License : http://www.opensource.org/licenses/bsd-license.php
//
// Copyright (c) 2008, Brothers In Arms/Burl Nyswonger
//
// See: BiaVote.uc for full License
// 
//@L

class BiaVoteClient extends ReplicationInfo;


/* ------------------------------------------------------------------------- */

`include( BiaVote/Classes/BiaStdInc.uci );
`include( BiaVote/Classes/BiaVote.uci );

/* ------------------------------------------------------------------------- */

// server side only
var int         SyncedToListVer, SyncedToDLListVer, SyncedToVoteListVer;
var int         nListVer, nDLListVer, nVoteListVer;

var int         UIListIdx, UIListLastIdx;
var bool        bSyncVotes, bClientHasOwner;
var int         SendIdx;
var BiaVote      BiaVoteMaster;
var int         ClientSyncNaks;
var float       fTimeSyncStart;

// client-side only...
var BiaVoteScene             MyScene;
var bool                     bUIActive, bFirstSync;
var int                      lastclock;
var BiaVoteInteraction       BiaVoteIA;
var name                     BiaVoteHotKey;


// both client/server - sync'd via functions...
var array<sUIList>          UILists;
var PlayerController        MyPlayerController;
var int                     nSyncVer; // version of the "total sync" list...
var bool                    bMatchOver, bClientHasInteraction, bOnlyVotes;

// Consts...
const sMsgBadStateFunc =    " badstate - called outside of applicable state";



// ----------------------------------------------------------------------------

auto state ClientInit
{
    event BeginState( name PrevState )
    {
        bClientHasOwner = false;
        nListVer=-42;
        nDLListVer=-42;
        nVoteListVer=-42;
        nSyncVer=-42;
        SetTimer(0.33, true);
    }
    event Timer()
    {
        if( ROLE == ROLE_Authority )
        {
            if( MyPlayerController == None )
            {
                MyPlayerController = PlayerController(Owner);
                `BiaDbg("Set MyPlayerController to: " $ MyPlayerController);
                ClientSetPlayerController( MyPlayerController );
            }
            else
            {
                ClientSetPlayerController( MyPlayerController );
            }
            if( BiaVoteMaster == None )
            {
                BiaVoteMaster = class'BiaVote.BiaVote'.static.GetInstance();
                `BiaDbg("Set BiaVoteMaster to: " $ BiaVoteMaster );
            }
            if(BiaVoteMaster != None && MyPlayerController != None && bClientHasOwner )
            {
                GotoState('InitWait');
            }
            else
            {
                `BiaDbg("cond: " $ BiaVoteMaster $"/"$ MyPlayerController $"/"$ bClientHasOwner );
            }
        }
    }
    event ContinuedState()
    {
        `BiaError("ContinuedState (auto)");
    }
}


state InitWait
{
    function BeginState( name PrevState )
    {
        `BiaEnter();
        ShowWelcomeScreen();
        SetTimer(20.0, false, 'SetClientInteraction');
        SetTimer(15.0, false, 'TransitionToVoteWait');
    }
}

function TransitionToVoteWait()
{
    `BiaDbg("Transitioning to VoteWait");
    //TODO: this is where the Interaction should be set up - not before...
//    ClientSetupInteraction( MyPlayerController );
    GotoState('VoteWait');
}

reliable server function ServerAckClientHasInteraction()
{
    `BiaEnter();
    bClientHasInteraction = true;
    ClearTimer('SetClientInteraction');
}

reliable client function ClientSetClientInteraction()
{
    `BiaEnter();
    ClientSetupInteraction( MyPlayerController );
}

function SetClientInteraction()
{
    `BiaEnter();
    if( ! bClientHasInteraction )
    {
        ClientSetClientInteraction();
        SetTimer(1.0, false, 'SetClientInteraction');
        return;
    }
}

//
// this is f'd up... The dataclient should just sync the data...
// Need to move that other code out of there...
function ShowWelcomeScreen( )
{
    local BiaVoteDataClient dcl;
    
    if( MyPlayerController != none ) 
    {
        `BiaDbg("Spawing BiaVoteDataClient...");
        dcl = Spawn(class'BiaVoteDataClient', MyPlayerController );
        `BiaDbg("dcl: " $ dcl);
        if( dcl != None )
        {
            dcl.Initialize( MyPlayerController );
        }
    }
}


// ----------------------------------------------------------------------------

//TODO: need to fix up the restart logic -- should set a flag that we are
// restarting, etc. - get rid of some of these timers!
function DoRestart(string sMessage)
{
    `BiaDbg("Sending Client Message: " $ sMessage);
    ClientDoRestart(sMessage);
    SetTimer(2.0, false, 'SwitchToRestart');
}

function SwitchToRestart()
{
    GotoState('Restarting');
}

//
//TODO: move the messaging to a utility class, or make static in message class.
reliable client function ClientDoRestart(string sMessage)
{
    local BiaVoteMessageObject Obj;

    `BiaDbg("sMessage: " $ sMessage);
    
    if( MyScene != None )
    {
        MyScene.CloseScene(MyScene);
    }
    
    if( MyPlayerController != None )
    {
        Obj = new class 'BiaVote.BiaVoteMessageObject';
        if( Obj != None )
        {
            `BiaDbg("Sending Message: " $ sMessage);
            Obj.Message = sMessage;
            
            //TODO: need to find a better way to render on the HUD when
            //  the match is over -- this works for now -- probably just
            //  need to use a UIScene, could use a UTDrawPanel...
            UTHud(MyPlayerController.myHUD).UTGRI.bMatchIsOver = false;
            
            MyPlayerController.ReceiveLocalizedMessage(class'BiaVoteEndMessage', 0, None, None, Obj );
        }
        else
        {
            `BiaError("Unable to send Message: new object creation failed.");
        }
    }
    else
    {
        `BiaError("Unable to send Message: No PlayerController");
    }
}

state Restarting
{
    reliable client function ClientOpenUI();
    simulated function BiaVoteOpenUI();
    function DoMapVote();
    
    function BeginState( name PrevState )
    {
        ClearTimer('ClientSync');
        ClearTimer();
        UnRef();        //wtf:?
    }
}

function LogOut()
{
    `BiaLog("LogOut, inactive...");
    // for now, just go inactive and get trash-collected later...
    ClearTimer();
    MyScene = None;
    bUIActive = false;
    GotoState('');
    UnRef();
    //TODO: unregister with BiaVoteMaster, etc....
    //TODO: what about spectators???
}

reliable server function ServerUIActivated()
{
    bUIActive = true;
    if( ! NeedToSync() )
    {
        ClientAckSync("", nSyncVer, false);
    }
}

reliable client function UIActivated( `BiaVoteClientUIClass Scene )
{
    nSyncVer = -1;
    bUIActive = true;
    MyScene = Scene;
    ServerUIActivated();
}

reliable server function ServerUIDeactivated()
{
    bUIActive = false;
}

reliable client function UIDeactivated()
{
    bUIActive = false;
    MyScene = None;
    ServerUIDeactivated();
}


// "global" functions for state code.
function Timer()
{
}

reliable server function ServerAck( int Idx )
{
    ClearTimer('ClientSync');
    `BiaError(sMsgBadStateFunc);
}    

function int GetNextUnSynced( int from )
{
    ClearTimer('ClientSync');
    `BiaError(sMsgBadStateFunc);
    return -1;
}

singular reliable server function ClientSync()
{
    ClearTimer('ClientSync');
    `BiaError(sMsgBadStateFunc);
}


function SyncDone( bool bSucces, optional string sUIMsg="" )
{
    ClearTimer('ClientSync');
    `BiaError(sMsgBadStateFunc);
}


// ----------------------------------------------------------------------------

function NewVoteRound(optional bool bGameChange)
{
    nListVer = -1;
}

function ServerReSync()
{
    //TODO: do I need this any more?
}
reliable client function ClientReSync()
{
    //TODO: do I need this any more?
}

function int VoteTimeRemaining()
{
    local float left;
    if( BiaVoteMaster != None )
    {
        if( BiaVoteMaster.BiaVI.bVoting )
        {
            left = BiaVoteMaster.BiaVI.TimeEnd - WorldInfo.TimeSeconds;
        } else {
            left = ( BiaVoteMaster.BiaVI.TimeEnd - BiaVoteMaster.BiaVI.TimeStart );
        }
    }
    
    return( max( 0, int(left)) );
}

state VoteWait
{    
    function BeginState( Name PrevState )
    {
        `BiaDbg("state: VoteWait");
    }
    function Timer()
    {
        if( NeedToSync() )
        {
            PushState('Sync');
        }
        else
        {
            ClientServiceUI( VoteTimeRemaining(), nSyncVer );
        }
    }
    reliable server function SyncVotes()
    {
        `BiaDbg("I am told to sync votes...");
        bSyncVotes = true;
        PushState('Sync');
    }
    event PushedState()
    {
        `BiaDbg("State: VoteWait");
    }
    event PoppedState()
    {
        `BiaDbg("State: VoteWait");
    }    
    event ContinuedState()
    {
        `BiaDbg("State: VoteWait");
    }        
}

//TODO:
//   Dynamically adjust the timer to optimal...  Start off with something based
//   on the ping-rate (how?) - and adjust up/down depending on timeouts/naks
//
//TODO:
//   Record the number of failed sync's - don't let the server spin if they
//   keep failing for whatever reason....
//
state Sync
{
    reliable server function SyncVotes()
    {
        `BiaDbg("I am told to sync votes - but I'm already doing it!");
        // nothing...
    }
    
    function NewVoteRound(optional bool bGameChange )
    {
        nListVer = -1;
    }
    
    function PoppedState( )
    {
        ClearTimer('ClientSync');
        `BiaDbg("Total Sync Time: " $ WorldInfo.TimeSeconds - fTimeSyncStart);
        if( bSyncVotes )
        {
            UISyncVotes();
            bSyncVotes = false;
        }
        `BiaDbg("And again...   : " $ WorldInfo.TimeSeconds - fTimeSyncStart);
    }
    function PushedState( )
    {
        local int nItems, nMaps;
        local bool pop;

        `BiaDbg("ListVers: "$SyncedToListVer$"/"$nListVer$
                ", "$SyncedToDLListVer$"/"$nDLListVer$
                ", "$SyncedToVoteListVer$"/"$nVoteListVer);

        fTimeSyncStart = WorldInfo.TimeSeconds;
        
        `BiaDbg("State: Sync");
        
        pop = false;
        nItems = CreateListDelta( nMaps );
        if( nItems < 1 )
        {
            `BiaDbg("No Items to sync");
            pop = true;
        }
        
        if ( !pop )
        {
            UIListIdx = GetNextUnSynced(0);
            if( UIListIdx < 0 )
            {
                `BiaDbg("GetNextUnSynced() says nothing to sync");
                pop = true;
            }
        }
        
        if ( !pop )
        {
            if( BiaVoteMaster != None )
            {
                UIListLastIdx = -1;
                ClientSyncNaks = 0;
                if( ! bSyncVotes )
                {
                    `BiaDbg("bOnlyVotes is: " $ bOnlyVotes);
                    if( nMaps > 0 )
                    {
                        ClientStartSync("Receiving " $ nMaps $ " Maps for Game: " $ CurVGame(), bOnlyVotes, nMaps != 0);
                    }
                    else
                    {
                        ClientStartSync("Receiving List Update...", bOnlyVotes, false);                        
                    }
                }
                SetTimer(0.1, true, 'ClientSync');
            }
            else
            {
                `BiaError("BiaVoteMaster has gone away!");
                pop = true;
            }
        }
        if( pop )
        {
            SyncDone(true);
        }
    }
    function Timer()
    {
    }
    function SyncDone( bool bSuccess, optional string sUIMsg="" )
    {
        ClearTimer('ClientSync');
        if( bSuccess )
        {
            `BiaDbg("ListVers: "$SyncedToListVer$"/"$nListVer$
                    ", "$SyncedToDLListVer$"/"$nDLListVer$
                    ", "$SyncedToVoteListVer$"/"$nVoteListVer);
            
            nListVer = SyncedToListVer;
            nDLListVer = SyncedToDLListVer;
            nVoteListVer = SyncedToVoteListVer;
            nSyncVer += 1;
            
            `BiaDbg("acking sync - bOnlyVotes is: " $ bOnlyVotes);
            
            ClientAckSync(sUIMsg == "" ? "Vote!" : sUIMsg, nSyncVer, bOnlyVotes);

            `BiaDbg("ListVers: "$SyncedToListVer$"/"$nListVer$
                    ", "$SyncedToDLListVer$"/"$nDLListVer$
                    ", "$SyncedToVoteListVer$"/"$nVoteListVer);
            
        }
        else
        {
            ClientNakSync(sUIMsg == "" ? "Sync Error" : sUIMsg);
        }
        bSyncVotes=false;
        PopState();
    }
    singular reliable server function ClientSync()
    {
        if( UIListIdx == UIListLastIdx )
        {
            // already sent - no ack yet... ?
            ClientSyncNaks += 1;
            if( ClientSyncNaks > 10 )
            {
                `BiaWarn("Giving up on sync - client is not Acking the server");
                SyncDone(false, "Error syncing.  Close/re-open BiaVote to retry");
            }
            return;
        }
        if( UIListIdx < 0 )
        {
            SyncDone(true, "Vote!");
            return;
        }
        if(BiaVoteMaster != None && BiaVoteMaster.bVoteReady)
        {
            if( UIListIdx < UILists.Length )
            {
                `BiaDbg("sending item: " $
                        UILists[UIListIdx].Id $ ", " $
                        UILists[UIListIdx].vType $ ", " $
                        UILists[UIListIdx].Title );
                
                UIListLastIdx = UIListIdx;
                ClientSyncNaks = 0;
                
                //TODO: sync the clock here as well... - but debounce it a little!!
                // use fTimeSyncStart to decide when/if we need to call VoteTimeRemaining() ...
                ClientAddUIList( UILists[UIListIdx], UIListIdx );
            }
        } else {
            `BiaDbg("bVoteReady != true");
            ClientSyncNaks += 1;
            return;
        }
    }
    reliable server function ServerAck(int Idx)
    {
        `BiaDbg("Ack from client");
        if( Idx == UIListIdx )
        {
            UILists[UIListIdx].bSynced = true;
            if(UILists[UIListIdx].vType == vtNone)
            {
                UILists[UIListIdx].Id = 0;
            }
            UIListIdx = GetNextUnSynced( UIListIdx + 1 );
        }
        if( UIListIdx == -1 || UIListIdx >= UILists.Length )
        {
            SyncDone(true, "Vote!");
            return;
        }
    }
    event ContinuedState()
    {
        `BiaDbg("Continued State: Sync");
    }
    function int GetNextUnSynced( int from )
    {
        local int i;
        
        if( from > -1 && from < UILists.Length )
        {
            for(i = from; i < UILists.Length; i++)
            {
                if(UILists[i].Id < 1)
                {
                    continue;
                }
                if(UILists[i].bSynced == false)
                {
                    return(i);
                }
            }
        }
        return -1;
    }
}



// ----------------------------------------------------------------------------

function string CurVGame()
{
    if( BiaVoteMaster != None )
    {
        return BiaVoteMaster.CurVGame;
    }
    return "Default Game";
}
function bool NeedToSync()
{
    if( BiaVoteMaster != None )
    {
        if(BiaVoteMaster.nListVer != nListVer ||
           BiaVoteMaster.nDLListVer != nDLListVer ||
           BiaVoteMaster.nVoteListVer != nVoteListVer )
        {
            //
            // it is assumed that if we return true here that a sync will
            // occur very shortly afterword...
            // So, we use the values we just checked at the end of
            // a *Successful* sync with the client side... - this way,
            // if things change during the sync, we won't miss out...
            //
            SyncedToListVer = BiaVoteMaster.nListVer;
            SyncedToDLListVer = BiaVoteMaster.nDLListVer;
            SyncedToVoteListVer = BiaVoteMaster.nVoteListVer;
            
            `BiaDbg("need sync: "$SyncedToListVer$"/"$nListVer$
                    ", "$SyncedToDLListVer$"/"$nDLListVer$
                    ", "$SyncedToVoteListVer$"/"$nVoteListVer);
            return true;
        }
    }
    return false;
}

function CombineLists( out array <sUIList> lst, bool bUI, bool bDL, bool bVotes )
{
    local sUIList uil;
    local int vidx, idx;

    lst.Length = 0;
    
    if( bUI && BiaVoteMaster != None )
    {
        foreach BiaVoteMaster.UILists(uil)
        {
            if( uil.Id > 0 ) {
                lst.AddItem(uil);
            }
        }
    }
    if( bDL && BiaVoteMaster != None )
    {
        foreach BiaVoteMaster.biaDL(uil)
        {
            if( uil.Id > 0 )
            {
                lst.AddItem(uil);
            }
        }
    }
    if( bVotes && BiaVoteMaster != None )
    {
        for(vidx = 0; vidx < BiaVoteMaster.aBiaVotes.Length; vidx++)
        {
            if( BiaVoteMaster.aBiaVotes[vidx].Id > 0 && BiaVoteMaster.aBiaVotes[vidx].vType != vtNone )
            {
                idx = lst.Length;
                lst.Add(1);
                lst[idx].vType = vtVote;
                lst[idx].Id = BiaVoteMaster.aBiaVotes[vidx].Id | 16384;
                lst[idx].nVotes = BiaVoteMaster.aBiaVotes[vidx].nVotes;
                lst[idx].Title = BiaVoteMaster.aBiaVotes[vidx].Title;
            }
        }
    }
}

//TODO:
//  only create deltas for lists that have changed... 
function int CreateListDelta( out int nMaps )
{
    local int hisIdx, myIdx, nRelevent;
    local array<int> sawId;
    local array<sUIList> lstTmp;
    local bool bSetOnlyVotes;

    `BiaEnter();

    nMaps = 0;    
    nRelevent = 0;
    sawId.Length = 0;
    bSetOnlyVotes = true;

    lstTmp.Length = 0;
    
    CombineLists(lstTmp, true, true, true);
    
    for(hisIdx = 0; hisIdx < lstTmp.Length; hisIdx++)
    {
        myIdx = UILists.Find('Id', lstTmp[hisIdx].Id);
        if( myIdx < 0 )
        {
            // Id's are set to 0 when Ack'd and vType==vtNone
            myIdx = UILists.Find('Id', 0);
            if( myIdx < 0 )
            {
                myIdx = UILists.Length;
                UILists.Add(1);
            }
            UILists[myIdx].bSynced = false;
            UILists[myIdx].Id = lstTmp[hisIdx].Id;
            UILists[myIdx].nVotes = lstTmp[hisIdx].nVotes;
            UILists[myIdx].Title = lstTmp[hisIdx].Title;
            UILists[myIdx].vType = lstTmp[hisIdx].vType;
            if( UILists[myIdx].vType != vtVote ) { bSetOnlyVotes = false; }
            if( UILists[myIdx].vType == vtMap ) { nMaps += 1; }
            nRelevent += 1;
        }
        else
        {
            if( UILists[myIdx].vType == vtVote )
            {
                if( UILists[myIdx].nVotes != lstTmp[hisIdx].nVotes )
                {
                    UILists[myIdx].nVotes = lstTmp[hisIdx].nVotes;
                    UILists[myIdx].bSynced = false;
                    // its always a vote
                    nRelevent += 1;
                }
            }
        }
        sawId.AddItem( lstTmp[hisIdx].Id );
    }
    
    for(myIdx = 0; myIdx < UILists.Length; myIdx++)
    {
        if( UILists[myIdx].Id < 1 )
        {
            continue;
        }
        if( sawId.Find( UILists[myIdx].Id ) > -1 )
        {
            continue;
        }
        
        if( UILists[myIdx].vType != vtVote ) { bSetOnlyVotes = false; }
        
        `BiaDbg("Item is no longer relevent: " $ UILists[myIdx].Id);
        UILists[myIdx].bSynced = false;
        UILists[myIdx].Title = "";
        UILists[myIdx].vType = vtNone;
        UILists[myIdx].nVotes = 0;
        nRelevent += 1;
    }
    
    `BiaDbg("UILists.Length == " $ UILists.Length $ ", nRelevent == " $ nRelevent);
    
    bOnlyVotes = bSetOnlyVotes;
    `BiaExit();
    return(nRelevent);
}

reliable server function SyncVotes()
{
    `BiaError(sMsgBadStateFunc);
}


// caller is singular...
simulated reliable client function ClientAddUIList( sUIList s, int Idx )
{
    local int Index;
    
    `BiaDbg("Adding Item: " $ s.Id $ ", " $ s.vType $ ", " $ s.Title);
    
    if( s.vType == vtNone )
    {
        Index = UILists.Find('Id', s.Id);
        if( Index > -1 )
        {
            `BiaDbg("Removing item from list.");
            UILists[Index].vType = vtNone;
            UILists[Index].Id = 0;
            UILists[Index].nVotes = 0;
            UILists[Index].Title = "";
        }
    } else {
        Index = UILists.Find('Id', s.Id);
        if( Index < 0 )
        {
            Index = UILists.Find('Id', 0);
            if( Index < 0 )
            {
                Index = UILists.Length;
                UILists.Add(1);
                `BiaDbg("Inserted new item");
            }
        }
        UILists[Index].vType = s.vType;
        UILists[Index].Id = s.Id;
        UILists[Index].nVotes = s.nVotes;
        UILists[Index].Title = s.Title;
        `BiaDbg("Updated element " $ Index $ ", " $ UILists[Index].Title);
    }
    ServerAck( Idx );
}



// -------------------------------------------------------------------------

reliable client function UISyncVotes( )
{
    if( bUIActive && MyScene != None )
    {
        MyScene.ReSync( UILists, true );
    }
}

reliable client function ClientServiceUI( int Clock, int nVer )
{
    if( bUIActive && MyScene != None ) {
        if( nVer != nSyncVer || nVer != MyScene.nSyncVer )
        {
            `BiaDbg("Syncing because something changed...");
            nSyncVer = nVer;
            MyScene.nSyncVer = nVer;   // TODO: must fix - this is kinda wrong...
            MyScene.ReSync( UILists, bOnlyVotes );
        }
        if( Clock != lastclock )
        {
            `BiaDbg("sync clock: " $ Clock);
            lastclock = Clock;
            MyScene.TickTock( Clock );
        }
    }
}

reliable client function ClientNakSync( string msg )
{
    `BiaError("Got a Nak from the Sync operation!");
    if( bUIActive && MyScene != None )
    {
        MyScene.ClientNakSync( msg );
    }
}
reliable client function ClientAckSync( string msg, int nVer, bool bSetOnlyVotes )
{
    nSyncVer = nVer;
    
    bOnlyVotes = bSetOnlyVotes;
    
    `BiaDbg("Got an Ack from the Sync operation - bOnlyVotes: " $ bOnlyVotes);
    if( bUIActive && MyScene != None )
    {
        `BiaDbg("Acking sync to UI - bSetOnlyVotes is: " $ bSetOnlyVotes);
        MyScene.ClientAckSync( msg, bSetOnlyVotes );
    }
}
reliable client function ClientStartSync( string msg, bool bSetOnlyVotes, optional bool bNewMaps = false)
{
    `BiaDbg("bSetOnlyVotes is: " $ bSetOnlyVotes);
    if( bUIActive && MyScene != None )
    {
        MyScene.ClientStartSync( bSetOnlyVotes ? "" : msg, bNewMaps );
    }
}


// --------------------------------------------------------------------------


function DoMapVote()
{
    `BiaEnter();
    ClientOpenUI();
    `BiaExit();
}

reliable client function ClientOpenUI()
{
    `BiaEnter();
    BiaVoteOpenUI();
    `BiaExit();
}

simulated function BiaVoteOpenUI()
{
    local UIInteraction UIController;
    
    if( ! bFirstSync )
    {
        //here: bFirstSync
    }
    
    if( ROLE < ROLE_Authority )
    {
        UIController = MyPlayerController.GetUIController();
        if ( UIController != None )
        {
            `BiaDbg("Opening UIScene: BiaVoteMenu");
            MyPlayerController.myHUD.SetShowScores(false);
            UIController.OpenScene( `BiaVoteClientUISceneClass, LocalPlayer(MyPlayerController.Player) );
        }
        else
        {
            `BiaError("can't get scene client");
        }
    }
}
simulated function ClientSetupInteraction( PlayerController PC )
{
    local BiaVoteDataStore DS;
    local UIProviderScriptFieldValue U;
        
    if( ROLE >= ROLE_Authority )
    {
        return;
    }

    if( bClientHasInteraction )
    {
        ServerAckClientHasInteraction();
        return;
    }
    
    if( BiaVoteIA == None )
    {
        if (LocalPlayer(PC.Player) != None && LocalPlayer(PC.Player).ViewportClient != None)
        {
            BiaVoteIA = new class'BiaVote.BiaVoteInteraction';
            if (BiaVoteIA != None )
            {
                bClientHasInteraction = true;
                ServerAckClientHasInteraction();

                `BiaLog("[BIA]Vote Version " $ `BiaVoteVersion );

                DS = class'BiaVote.BiaVoteDataClient'.static.GetDs( MyPlayerController );
                if( DS.GetFieldValue("VoteKey", U, 0) )
                {
                    if( U.StringValue != "" && name(U.StringValue) != '')
                    {
                        BiaVoteHotKey = name(U.StringValue);
                        `BiaLog("[BIA]Vote Key is: " $ BiaVoteHotKey);
                    }
                    else
                    {
                        `BiaError("Unable to retrieve BiaVoteHotKey from BiaVoteDataStore");
                    }
                }
                
                `BiaDbg("creating new Interaction for client");
                BiaVoteIA.MyPlayerController = PC;
                BiaVoteIA.BVC = self;
                BiaVoteIA.BiaVoteHotKey = BiaVoteHotKey;
                LocalPlayer(PC.Player).ViewportClient.InsertInteraction(BiaVoteIA);
                
                PC.ReceiveLocalizedMessage(class'BiaVoteMessage', EBiaVoteMessage_KeyMapped);
                return;
            }
        }
        `BiaError("Unable to create new Interaction object (BiaVote.BiaVoteInteraction)");
    }
}

/*
simulated function ClientLocalMessage( EBiaVoteMessage msgnum )
{
// Hmmm... this works fine for client-side dynamic messages - but not server-sent (can't pass objects :-( )
//    local SeqVar_Union U;
//    U = new class'Engine.SeqVar_Union';
//    U.StringValue="this is a test... blah blah...";
//    `BiaDbg("msgnum:" $ msgnum );
    MyPlayerController.ReceiveLocalizedMessage(class'BiaVoteMessage',msgnum); // ,,,U);
}
*/

reliable server function ServerClientHasOwner()
{
    bClientHasOwner = true;
}

reliable client function ClientSetPlayerController( PlayerController PC )
{
    `BiaEnter();
    if( PC != None )
    {
        if( MyPlayerController == None )
        {
            MyPlayerController = PC;
            `BiaDbg("set MyPlayerController to: " $ PC);
            
            
//TODO: Move this to some other place -should happen 5-10 seconds after game starts...
//x:mut            ClientSetupInteraction(PC);
            
        }
        else
        {
            ServerClientHasOwner();
        }
    }
    `BiaExit();
}


// --------------------------------------------------------------------------

reliable server function ServerSubmitVote( int Id )
{
    if( BiaVoteMaster != None )
    {
        BiaVoteMaster.SubmitVote(Id, MyPlayerController, self );
    }
}
reliable client function SubmitVote( int Id )
{
    ServerSubmitVote(Id);
}
reliable client function ClientAckVote( string msg )
{
    if( MyScene != None && bUIActive )
    {
        MyScene.AckVote(msg);
    }
    else
    {
        `BiaLog("TODO: vote-ack/nak to console...");
    }
}
function AckVote( string msg )
{
    ClientAckVote( msg );   
}

// ---------------------------------------------------------------------------

reliable client function ClientLogUILists()
{
`if(`isdefined(DEBUG))
    local sUIList u;
    foreach UILists( u )
    {
        `BiaDbg("UIList: " $ u.vType $ "), Title(" $ u.Title $")");
    }
`endif
}


event Reset()
{
    `BiaEnter();
    `BiaDbg("Reset(" $ self $ ") - BiaVoteClient");
    Super.Reset();
    `BiaExit();
}

reliable client function ClientUnRef()
{
    return; //x:mut

    `BiaEnter();
    if( MyScene != None )
    {
        MyScene.UnRef();
    }
    if( BiaVoteIA != None )
    {
        BiaVoteIA.BVC = None;
        BiaVoteIA.MyPlayerController = None;
        BiaVoteIA = None;
    }
    MyScene = None;
    MyPlayerController = None;
    BiaVoteMaster = None;
    `BiaExit();
}
function UnRef()
{
    return; //x:mut
    
    `BiaEnter();
    MyScene = None;
    MyPlayerController = None;
    BiaVoteMaster = None;
    ClientUnRef();
    ClearTimer();
    GotoState('');
    `BiaExit();
}
event Destroyed()
{
    `BiaEnter();
    `BiaDbg("Destroyed(" $ self $ ") - BiaVoteClient");
    Super.Destroyed();
    
    return; //x:mut

    UnRef();
    `BiaExit();
}

event MatchOver() //x:mut
{
    bMatchOver = true;
    ClientMatchOver();
}

reliable client function ClientMatchOver() //x:mut
{
    local UTGameReplicationInfo GRI;
    bMatchOver = true;
    if( MyPlayerController != None )
    {
        `BiaAssert( MyPlayerController.IsInState('RoundEnded') );
        MyPlayerController.ClearTimer('ShowScoreboard');
        GRI = UTGameReplicationInfo(WorldInfo.GRI);
        `BiaAssert( GRI != None );
        GRI.bStoryMode = true;
    }
}


defaultproperties
{
    RemoteRole=ROLE_SimulatedProxy
    bAlwaysRelevant=True
    bSkipActorPropertyReplication=false
    Role=ROLE_Authority
    NetUpdateFrequency=1
    

    //x:mut
    bMatchOver=false
    
    bFirstSync=false    
    bUIActive=false
    lastclock=-1

    // Important!
    nListVer=-1
    nDLListVer=-1
    nVoteListVer=-1
    
}
